package com.kmxd.ams.app.archive;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kmxd.ams.app.archive.command.ArchRuleInitCmd;
import com.kmxd.ams.client.archive.*;
import com.kmxd.ams.client.archive.dto.*;
import com.kmxd.ams.client.archive.vo.ArchiveRuleFieldListVO;
import com.kmxd.ams.client.archive.vo.ArchiveRuleMainListVO;
import com.kmxd.ams.client.archive.vo.ArchiveRuleMainVO;
import com.kmxd.ams.core.common.constant.ArchConst;
import com.kmxd.ams.core.common.constant.CacheConst;
import com.kmxd.ams.core.common.constant.Constants;
import com.kmxd.ams.core.common.query.QueryGen;
import com.kmxd.ams.core.util.ConvertUtil;
import com.kmxd.ams.core.util.IdUtil;
import com.kmxd.ams.infra.archive.entity.*;
import com.kmxd.ams.infra.archive.mapper.ArchiveRuleMainMapper;
import com.kmxd.ams.infra.tmpl.dto.*;
import com.kmxd.ams.infra.tmpl.util.TmplUtils;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import org.apache.commons.compress.utils.Lists;
import org.jetbrains.annotations.NotNull;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 整理规则 服务实现类
 *
 * @author nickbi
 * @since 2022-07-30
 */
@Service
@AllArgsConstructor
@CacheConfig(cacheNames = CacheConst.CACHE_PREFIX_ARCHIVE_RULE_MAIN)
public class ArchiveRuleMainSvcImpl extends ServiceImpl<ArchiveRuleMainMapper, ArchiveRuleMain>
    implements IArchiveRuleMainSvc {

  private IArchiveRuleCatalogSvc archiveRuleCatalogSvc;
  private ArchiveRuleMainMapper archiveRuleMainMapper;
  private IArchiveOrderFieldSvc archiveOrderFieldSvc;
  private IArchiveRuleFieldSvc archiveRuleFieldSvc;
  private IArchiveFieldSvc archiveFieldSvc;
  private ArchRuleInitCmd archTypeInitCmd;
  private TmplUtils tmplUtils;

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public Page<ArchiveRuleMainListVO> selectPage(Page reqPage, ArchiveRuleMainQueryDTO req) {
    LambdaQueryWrapper<ArchiveRuleMain> queryWrapper =
        QueryGen.init(new QueryWrapper<ArchiveRuleMain>(), req).lambda();
    queryWrapper.orderByDesc(ArchiveRuleMain::getValidYear);
    Page<ArchiveRuleMain> page = archiveRuleMainMapper.selectPage(reqPage, queryWrapper);
    Page<ArchiveRuleMainListVO> voPage = BeanUtil.copyProperties(page, Page.class);
    voPage.setRecords(BeanUtil.copyToList(page.getRecords(), ArchiveRuleMainListVO.class));
    return voPage;
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public List<ArchiveRuleMainListVO> selectList(ArchiveRuleMainQueryDTO req) {
    LambdaQueryWrapper<ArchiveRuleMain> queryWrapper =
        QueryGen.init(new QueryWrapper<ArchiveRuleMain>(), req).lambda();
    queryWrapper.orderByDesc(ArchiveRuleMain::getValidYear);
    List<ArchiveRuleMain> list = archiveRuleMainMapper.selectList(queryWrapper);
    return BeanUtil.copyToList(list, ArchiveRuleMainListVO.class);
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public List<ArchiveRuleMainListVO> selectListAll() {
    LambdaQueryWrapper<ArchiveRuleMain> queryWrapper = Wrappers.lambdaQuery();
    queryWrapper.orderByDesc(ArchiveRuleMain::getValidYear);
    List<ArchiveRuleMain> list = archiveRuleMainMapper.selectList(queryWrapper);
    return BeanUtil.copyToList(list, ArchiveRuleMainListVO.class);
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public ArchiveRuleMainListVO selectByVersion(ArchiveRuleMainQueryDTO req) {
    LambdaQueryWrapper<ArchiveRuleMain> wrapper = Wrappers.lambdaQuery();
    wrapper.eq(ArchiveRuleMain::getArchTypeId, req.getArchTypeId());
    wrapper.le(ArchiveRuleMain::getValidYear, req.getVersion());
    wrapper.orderByDesc(ArchiveRuleMain::getValidYear);
    wrapper.last(" limit 1");
    ArchiveRuleMain archiveRuleMain = archiveRuleMainMapper.selectOne(wrapper);
    return ConvertUtil.toBean(archiveRuleMain, ArchiveRuleMainListVO.class);
  }
  /**
   * 初始化门类的整理规则
   *
   * @param req
   */
  @Caching(
      evict = {
        @CacheEvict(allEntries = true),
        @CacheEvict(cacheNames = CacheConst.CACHE_PREFIX_ARCHIVE_RULE_FIELD, allEntries = true)
      })
  @Override
  @Transactional(rollbackFor = Exception.class)
  public void add(ArchiveRuleMainAddDTO req) {
    ArchiveRuleMain entity = BeanUtil.copyProperties(req, ArchiveRuleMain.class);
    entity.setId(IdWorker.getId());
    List<ArchTmplBO> tmpl = tmplUtils.getTmpl(req.getManageModel());
    entity.setValidYear(
        ObjectUtil.isEmpty(req.getValidYear()) ? Constants.DEF_VERSION : req.getValidYear());
    entity.setName(ObjectUtil.isEmpty(req.getName()) ? Constants.DEF_VERSION_NAME : req.getName());
    tmpl.stream()
        .filter(f -> null != f.getConfig())
        .forEach(
            t -> {
              if (t.type.equals(ArchTmplBO.DOCUMENT)) {
                DocumentRuleConfigBO config = (DocumentRuleConfigBO) t.getConfig();
                // 编号权限控制, 默认集中编号
                entity.setCollectWay(config.getCollectWay());
                entity.setCodeLen(config.getCodeLen());
                entity.setSealStyle(config.getSealStyle());
                entity.setEnableSeal(config.getEnableSeal());
                List<CodeFieldBO> codeFields = config.getCodeFieldList();
                List<ArchiveRuleField> codeFieldList =
                    buildArchRuleField(entity, req.getDocTableId(), codeFields);
                archiveRuleFieldSvc.saveBatch(codeFieldList);
                if (req.getInit()) {
                  // 初始化其他规则信息
                  archTypeInitCmd.initRule(req, config);
                  setCopyField(codeFieldList, req);
                  setOrderField(codeFieldList, req, false, false);
                }
              }

              if (t.type.equals(ArchTmplBO.VOLUME)) {
                VolumeRuleConfigBO config = (VolumeRuleConfigBO) t.getConfig();
                entity.setVolCodeLen(config.getVolCodeLen());
                entity.setVolPageNo(config.getVolPageNo());
                entity.setVolPageSep(config.getVolPageSep());
                entity.setVolAutoCode(config.getVolAutoCode());
                // 案卷档号规则
                List<CodeFieldBO> volCodeFields = config.getCodeFieldList();
                List<ArchiveRuleField> volCodeFieldList =
                    buildArchRuleField(entity, req.getVolTableId(), volCodeFields);
                archiveRuleFieldSvc.saveBatch(volCodeFieldList);
                if (req.getInit()) {
                  // 初始化其他规则信息 --初始化继承关系 修改文件级，设置表为案卷级
                  setExtendField(volCodeFieldList, req, false);
                  setCopyField(volCodeFieldList, req);
                  setOrderField(volCodeFieldList, req, false, true);
                }
              }
              if (t.type.equals(ArchTmplBO.BOX)) {
                if (req.getInit()) {
                  BoxRuleConfigBO config = (BoxRuleConfigBO) t.getConfig();
                  entity.setBoxCodeLen(config.getBoxCodeLen());
                  entity.setBoxOrg(config.getBoxOrg());
                  List<CodeFieldBO> codeFields = config.getCodeFieldList();
                  List<ArchiveRuleField> codeFieldList =
                      buildArchRuleField(entity, req.getDocTableId(), codeFields);
                  codeFieldList.forEach(
                      item ->
                          archiveRuleFieldSvc.update(
                              Wrappers.<ArchiveRuleField>lambdaUpdate()
                                  .set(
                                      ArchiveRuleField::getEnableBoxGroup, item.getEnableBoxGroup())
                                  .eq(ArchiveRuleField::getArchTypeId, entity.getArchTypeId())
                                  .eq(ArchiveRuleField::getTableId, req.getDocTableId())
                                  .eq(ArchiveRuleField::getFieldCode, item.getFieldCode())));
                  // 初始化其他规则信息 --初始化继承关系 修改文件级，设置表为盒
                  setExtendField(codeFieldList, req, true);
                  setCopyField(codeFieldList, req);
                  setOrderField(codeFieldList, req, true, false);
                }
              }
              //  档号结构，使用规则设置表
              if (t.type.equals(ArchTmplBO.PROJECT)) {
                if (req.getInit()) {
                  // 初始化目录设置
                  setOrderProjField(req);
                }
              }
            });
    archiveRuleMainMapper.insert(entity);
    // 初始化目录树
    if (req.getInit()) {
      initCatalog(req.getArchTypeId());
    }
  }

  /**
   * 设置继承字段
   *
   * @param codeFieldList
   * @param req
   * @param isBox
   */
  private void setExtendField(
      List<ArchiveRuleField> codeFieldList, ArchiveRuleMainAddDTO req, boolean isBox) {
    Set<String> fields =
        codeFieldList.stream()
            .filter(x -> ObjectUtil.notEqual(x.getFieldCode(), ArchConst.DOC_SEQUENCE_DB))
            .map(ArchiveRuleField::getFieldCode)
            .collect(Collectors.toSet());
    if (!fields.isEmpty()) {
      // 查询修改继承字段信息
      LambdaQueryWrapper<ArchiveField> queryWrapper = Wrappers.lambdaQuery();
      queryWrapper.eq(ArchiveField::getTableId, req.getDocTableId());
      queryWrapper.in(ArchiveField::getDbFieldKey, fields);
      List<ArchiveField> fieldList = archiveFieldSvc.list(queryWrapper);
      List<ArchiveField> updateField =
          fieldList.stream()
              .filter(
                  item ->
                      !item.getTableId()
                          .equalsIgnoreCase(isBox ? req.getBoxTableId() : req.getVolTableId()))
              .map(
                  field -> {
                    field.setAttrExtends(true);
                    field.setAttrUse(true);
                    field.setExtendsTableId(isBox ? req.getBoxTableId() : req.getVolTableId());
                    field.setExtendsField(StringUtils.camelToUnderline(field.getDbFieldKey()));
                    return field;
                  })
              .collect(Collectors.toList());
      archiveFieldSvc.updateBatchById(updateField);
    }
  }

  /**
   * 设置追加复制字段
   *
   * @param codeFieldList
   * @param req
   */
  private void setCopyField(List<ArchiveRuleField> codeFieldList, ArchiveRuleMainAddDTO req) {
    Set<String> fields =
        codeFieldList.stream()
            .filter(x -> ObjectUtil.notEqual(x.getFieldCode(), ArchConst.DOC_SEQUENCE_DB))
            .map(ArchiveRuleField::getFieldCode)
            .collect(Collectors.toSet());
    // 添加固定字段，题名、责任者
    fields.add("author");
    // 查询修改继承字段信息
    LambdaQueryWrapper<ArchiveField> queryWrapper = Wrappers.lambdaQuery();
    queryWrapper.eq(ArchiveField::getTableId, req.getDocTableId());
    queryWrapper.in(ArchiveField::getDbFieldKey, fields);
    List<ArchiveField> fieldList = archiveFieldSvc.list(queryWrapper);
    List<ArchiveField> updateField =
        fieldList.stream()
            .map(
                field -> {
                  field.setAttrCopy(true);
                  return field;
                })
            .collect(Collectors.toList());
    archiveFieldSvc.updateBatchById(updateField);
  }

  /**
   * 设置项目级排序
   *
   * @param req
   */
  private void setOrderProjField(ArchiveRuleMainAddDTO req) {
    // 项目案卷：项目级取年度降序，项目代号、项目名称升序
    List<ArchiveOrderField> orderFields = Lists.newArrayList();
    ArchiveOrderField yearField = new ArchiveOrderField();
    yearField.setArchTypeId(req.getArchTypeId());
    yearField.setTableId(req.getProjTableId());
    yearField.setOrderFieldCode("year");
    yearField.setOrderFieldName("年度");
    yearField.setOrderType("desc");

    ArchiveOrderField projCodeField = new ArchiveOrderField();
    projCodeField.setArchTypeId(req.getArchTypeId());
    projCodeField.setTableId(req.getProjTableId());
    projCodeField.setOrderFieldCode("proj_code");
    projCodeField.setOrderFieldName("项目代号");
    projCodeField.setOrderType("asc");

    ArchiveOrderField titleField = new ArchiveOrderField();
    titleField.setArchTypeId(req.getArchTypeId());
    titleField.setTableId(req.getProjTableId());
    titleField.setOrderFieldCode("title");
    titleField.setOrderFieldName("项目名称");
    titleField.setOrderType("asc");
    orderFields.add(yearField);
    orderFields.add(projCodeField);
    orderFields.add(titleField);

    archiveOrderFieldSvc.saveOrUpdateBatch(orderFields);
  }

  /**
   * 设置排序字段
   *
   * @param codeFieldList
   * @param req
   */
  private void setOrderField(
      List<ArchiveRuleField> codeFieldList,
      ArchiveRuleMainAddDTO req,
      boolean isBox,
      boolean isVol) {
    AtomicInteger index = new AtomicInteger(1);
    List<ArchiveOrderField> orderFields =
        codeFieldList.stream()
            .filter(
                x ->
                    ObjectUtil.notEqual(x.getFieldCode(), ArchConst.DOC_SEQUENCE_DB)
                        && ObjectUtil.notEqual(x.getFieldCode(), ArchConst.VOL_SEQUENCE_DB))
            .map(
                item -> {
                  ArchiveOrderField archiveOrderField = new ArchiveOrderField();
                  archiveOrderField.setArchTypeId(req.getArchTypeId());
                  archiveOrderField.setTableId(
                      !isBox && !isVol
                          ? req.getDocTableId()
                          : isBox ? req.getBoxTableId() : req.getVolTableId());
                  archiveOrderField.setOrderFieldCode(item.getFieldCode());
                  archiveOrderField.setOrderFieldName(item.getFieldName());
                  archiveOrderField.setOrderType("asc");
                  // 装盒：文件级：取档案号构成字段，升序，盒级：取档号构成字段（件号替换为盒流水），升序
                  if (isBox && item.getFieldCode().equals(ArchConst.DOC_SEQUENCE_DB)) {
                    archiveOrderField.setOrderFieldCode(ArchConst.DB_BOX_SEQUENCE);
                    archiveOrderField.setOrderFieldName("盒流水");
                  }
                  archiveOrderField.setSort(index.intValue());
                  index.getAndIncrement();
                  return archiveOrderField;
                })
            .collect(Collectors.toList());
    archiveOrderFieldSvc.saveOrUpdateBatch(orderFields);
  }

  /**
   * 初始化目录
   *
   * @param archTypeId
   */
  public void initCatalog(Long archTypeId) {
    archiveRuleCatalogSvc.initArchNode(archTypeId);
  }

  @NotNull
  private List<ArchiveRuleField> buildArchRuleField(
      ArchiveRuleMain entity, final String tableId, List<CodeFieldBO> codeFields) {
    return codeFields.stream()
        .map(
            f -> {
              ArchiveRuleField field = ConvertUtil.toBean(f, ArchiveRuleField.class);
              field.setArchTypeId(entity.getArchTypeId());
              field.setRuleId(entity.getId());
              field.setTableId(tableId);
              field.setEnableBoxGroup(f.getEnableBoxGroup());
              return field;
            })
        .collect(Collectors.toList());
  }

  @Caching(
      evict = {
        @CacheEvict(allEntries = true),
        @CacheEvict(cacheNames = CacheConst.CACHE_PREFIX_ARCHIVE_RULE_FIELD, allEntries = true)
      })
  @Override
  @Transactional(rollbackFor = Exception.class)
  public void edit(ArchiveRuleMainEditDTO req) {
    ArchiveRuleMain entity = BeanUtil.copyProperties(req, ArchiveRuleMain.class);
    archiveRuleMainMapper.updateById(entity);
  }

  @Caching(
      evict = {
        @CacheEvict(allEntries = true),
        @CacheEvict(cacheNames = CacheConst.CACHE_PREFIX_ARCHIVE_RULE_FIELD, allEntries = true)
      })
  @Override
  @Transactional(rollbackFor = Exception.class)
  public void remove(String ids) {
    Set<Long> idList = IdUtil.split(ids);
    this.baseMapper.deleteBatchIds(idList);
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public ArchiveRuleMainVO view(Long id) {
    ArchiveRuleMain entity = archiveRuleMainMapper.selectById(id);
    return BeanUtil.copyProperties(entity, ArchiveRuleMainVO.class);
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public ArchiveRuleMainListVO getRuleMainByVersion(RuleFieldQueryDTO req) {
    ArchiveRuleMainQueryDTO ruleMainQuery = new ArchiveRuleMainQueryDTO();
    ruleMainQuery.setArchTypeId(req.getArchTypeId());
    ruleMainQuery.setVersion(
        ObjectUtil.isNotEmpty(req.getYear()) ? req.getYear() : Constants.DEF_VERSION);
    ArchiveRuleMainListVO version = selectByVersion(ruleMainQuery);
    return version;
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public List<ArchiveRuleFieldListVO> getRuleFieldByVersion(RuleFieldQueryDTO req) {
    ArchiveRuleMainListVO ruleMainListVO =
        Objects.nonNull(req.getRuleId())
            ? ConvertUtil.toBean(view(req.getRuleId()), ArchiveRuleMainListVO.class)
            : getRuleMainByVersion(req);
    if (ObjectUtil.isNull(ruleMainListVO)) {
      return Lists.newArrayList();
    }
    ArchiveRuleFieldQueryDTO queryDTO =
        BeanUtil.copyProperties(req, ArchiveRuleFieldQueryDTO.class);
    queryDTO.setRuleId(ruleMainListVO.getId());
    if (BooleanUtil.isTrue(req.getHaveBox())) {
      queryDTO.setEnableBoxGroup(true);
      queryDTO.setTableId(null);
    }
    return archiveRuleFieldSvc.selectList(queryDTO);
  }
}
